package jumpd

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net"
	"os"

	"github.com/hirochachacha/go-smb2"
)

// type ConfigReadWriter interface {
// 	LoadConfig() bool
// 	Open() bool
// 	Size() (uint64, error)
// 	Read(buf []byte, offset uint64) (n int, err error)
// 	Write(buf []byte) (uint64, error)
// 	WriteAt(buf []byte, offset uint64) (uint64, error)
// 	Close()
// }

// Config 是用于存储配置的结构体
type SmbConfig struct {
	User           string `json:"user"`
	Password       string `json:"password"`
	Domain         string `json:"domain"`
	ServerIP       string `json:"serverIP"`
	ServerPort     int    `json:"serverPort"`
	ShareName      string `json:"shareName"`
	RemoteFilePath string `json:"remoteFilePath"`
}

type SmbLinkSet struct {
	TcpConn    net.Conn
	SmbDialer  *smb2.Dialer
	Session    *smb2.Session
	Share      *smb2.Share
	RemoteFile *smb2.File
}

func (s *Smb) Read(buf []byte, offset uint64) (n int, err error) {
	// not support
	return 0, nil
}

func (slink *SmbLinkSet) DeferAll() {
	if slink.TcpConn != nil {
		slink.TcpConn = nil
		defer slink.TcpConn.Close()
	}
	if slink.Session != nil {
		slink.Session = nil
		defer slink.Session.Logoff()
	}
	if slink.Share != nil {
		slink.Share = nil
		defer slink.Share.Umount()
	}
	if slink.RemoteFile != nil {
		slink.RemoteFile = nil
		defer slink.RemoteFile.Close()
	}
}

type Smb struct {
	Config  *SmbConfig
	LinkSet *SmbLinkSet
}

func (s *Smb) Size() (uint64, error) {
	endOffset, err := s.LinkSet.RemoteFile.Seek(0, io.SeekEnd)
	if err != nil {
		return 0, err
	}
	if endOffset < 0 {
		return 0, errors.New("end offset is less than 0")
	}
	return uint64(endOffset), nil
}

func (s *Smb) WriteAt(buf []byte, offset uint64) (uint64, error) {
	endOffset, err := s.LinkSet.RemoteFile.Seek(int64(offset), io.SeekStart)
	if err != nil {
		return 0, err
	}
	if endOffset < 0 {
		return 0, errors.New("end offset is less than 0")
	}

	local_write_n, err := s.LinkSet.RemoteFile.WriteAt(buf, endOffset)
	if err != nil {
		return 0, fmt.Errorf("write to smb error: %v", err)
	}

	return uint64(local_write_n), nil
}

func (s *Smb) Write(buf []byte) (uint64, error) {
	endOffset, err := s.LinkSet.RemoteFile.Seek(0, io.SeekEnd)
	if err != nil {
		return 0, err
	}
	if endOffset < 0 {
		return 0, errors.New("end offset is less than 0")
	}

	local_write_n, err := s.LinkSet.RemoteFile.WriteAt(buf, endOffset)
	if err != nil {
		return 0, fmt.Errorf("write to smb error: %v", err)
	}

	return uint64(local_write_n), nil
}

func (s *Smb) Close() {
	s.LinkSet.DeferAll()
}

func (s *Smb) Open() bool {
	if s.setup_smb_link() == nil {
		return true
	}
	// 出错, 则删除资源
	s.LinkSet.DeferAll()
	return false
}

func (s *Smb) setup_smb_link() error {
	config := s.Config

	s.LinkSet = &SmbLinkSet{}

	// 创建 TCP 连接
	tcpConn, err := net.Dial("tcp", fmt.Sprintf("%v:%v", config.ServerIP, config.ServerPort))
	if err != nil {
		return fmt.Errorf("connot dial tcp to smb server: %v", err)
	}
	s.LinkSet.TcpConn = tcpConn
	// defer tcpConn.Close()

	// 创建 SMB 客户端
	smbDialer := &smb2.Dialer{
		Initiator: &smb2.NTLMInitiator{
			User:     config.User,
			Password: config.Password,
			// Domain:   config.Domain,
		},
	}

	// 连接到 SMB 服务器
	session, err := smbDialer.Dial(tcpConn)
	if err != nil {
		return fmt.Errorf("connot dial smb link: %v", err)
	}
	s.LinkSet.Session = session
	// defer session.Logoff()

	// 打开共享
	share, err := session.Mount(config.ShareName)
	if err != nil {
		return fmt.Errorf("connot mount share: %v", err)
	}
	s.LinkSet.Share = share
	// defer share.Umount()

	// 打开远程文件
	remoteFile, err := share.Open(config.RemoteFilePath)
	if err != nil {
		return fmt.Errorf("connot open remote file: %v", err)
	}
	s.LinkSet.RemoteFile = remoteFile
	// defer remoteFile.Close()
	return nil
}

func (s *Smb) LoadConfig() bool {
	file, err := os.Open("smb_config.json")
	if err != nil {
		fmt.Printf("connot open config file %v", err)
		return false
	}
	defer file.Close()

	var config SmbConfig
	decoder := json.NewDecoder(file)
	err = decoder.Decode(&config)
	if err != nil {
		fmt.Printf("connot decode config file %v", err)
		return false
	}

	s.Config = &config
	fmt.Printf("smb config: %v\n", config)
	return true
}
